package com.seabook.imgdownloader.core;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.imageio.ImageIO;

public class ImgDownloader {
	
	private Map<URL, String> imgUrlMap = new LinkedHashMap<URL, String>();
	private Map<URL, String> reDownloadMap;

	private ImgSourceExtractor extractor;
	
	private File directoryLoc;
	private String url;
	
	private static final String BAK_FILE_NAME = "backup.data.txt";
	
	public ImgDownloader() {}

	public ImgDownloader(File directLoc, String url) {
		this.directoryLoc = directLoc;
		this.url = url;
	}
	
	public ImgDownloader(File directLoc, String url, String filter) {
		this.directoryLoc = directLoc;
		this.url = url;
	}

	public void downloadImages() {
		//Get the Img src link from the web url
		buildImgUrlMap();

		//Build up the File directory to make sure where the images to download to
		buildDirectory();

		//Download the pictures
		download();
	}
	
	public void downloadImagesFromBackupFile() {
		buildImgUrlMapFromBakFile();
		download();
	}

	private void buildImgUrlMapFromBakFile() {
		resetMaps();
		File file = new File(directoryLoc, BAK_FILE_NAME);
		FileReader fr = null;
		BufferedReader br = null;
		try {
			fr = new FileReader(file);
			br = new BufferedReader(fr);
			String url = "";
			while ((url = br.readLine()) != null) {
				initImgUrlMap(url);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fr != null) {
				try {
					fr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		reDownloadMap = new ConcurrentHashMap<URL, String>(imgUrlMap);
	}

	private void resetMaps() {
		if (imgUrlMap != null) {
			imgUrlMap.clear();
		}
		
		if (reDownloadMap != null) {
			reDownloadMap.clear();
			reDownloadMap = null;
		}
		
	}

	private void download() {
		boolean needReDownload = false;
		
		Set<URL> urlSet = imgUrlMap.keySet();
		for (URL url : urlSet) {
			String filename = imgUrlMap.get(url);
			URLConnection conn = null;
			InputStream is = null;
			try {
				conn = url.openConnection();
				is = conn.getInputStream();
				BufferedImage bi = ImageIO.read(is);
				File downloadFile = new File(directoryLoc, filename);
				ImageIO.write(bi, "JPG", downloadFile);
				reDownloadMap.remove(url);
				System.out.println("Image Downloaded! " + downloadFile.getPath());
			} catch (IOException e) {
				needReDownload = true;
				System.err.println(e.getMessage());
			} finally {
				try {
					if (is != null) {
						is.close();
					}
				} catch (IOException e) {
					//
				}
			}
		}
		
		if (needReDownload) {
			reDownload();
		} else
			System.out.println("Download Finished! Enjoy to see your pictures under: " + directoryLoc.getAbsolutePath());
	}
	
	

	private void reDownload() {
		int reDownloadTimes = 1;

		while (reDownloadTimes <= 3) {
			coolDown(reDownloadTimes);
			
			reDownloadTimes ++;
			Set<URL> urlSet = reDownloadMap.keySet();
			
			for (URL url : urlSet) {
				String filename = reDownloadMap.get(url);
				URLConnection conn = null;
				InputStream is = null;
				try {
					conn = url.openConnection();
					is = conn.getInputStream();
					BufferedImage bi = ImageIO.read(is);
					File downloadFile = new File(directoryLoc, filename);
					ImageIO.write(bi, "JPG", downloadFile);
					reDownloadMap.remove(url);
					System.out.println("Image Downloaded! " + downloadFile.getPath());
					if (reDownloadMap.isEmpty()) break;
					
				} catch (IOException e) {
					System.err.println(e.getMessage());
				} finally {
					try {
						if (is != null) {
							is.close();
						}
					} catch (IOException e) {
					}
				}
			}
		}
		
		if (reDownloadMap.size() > 0) {
			
			recordMissDownloadUrls2File();
			
			System.out.println("Redownloaded Finished. You Still have " + reDownloadMap.size());
			System.out.println("Try to download them at another time.");
			System.out.println("Please go to " + directoryLoc.getAbsolutePath() + " to check the images.");
		} else
			System.out.println("Redownloaded Finished. Please Check your pictures under: " + directoryLoc.getAbsolutePath() + " again.");
	}

	private void recordMissDownloadUrls2File() {
		File file = new File(directoryLoc, BAK_FILE_NAME);
		FileWriter fw = null;
		try {
			fw = new FileWriter(file);
			
			Set<URL> urlSet = reDownloadMap.keySet();
			for (URL url : urlSet) {
				fw.write(url.toString());
				fw.append("\n");
				fw.flush();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fw != null) {
				try {
					fw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
	}

	private void coolDown(int reDownloadTimes) {
		System.out.println("Re-Download Times: " + reDownloadTimes);
		System.out.println("Still have " + reDownloadMap.size() + " images to re-downlaod, be patient !!!");
		try {
			Thread.sleep(30000 * reDownloadTimes);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private void buildImgUrlMap() {
		extractor.extractImgSrcs(url);
		initImgUrlMap(extractor.getExtractorImgSrcs());
		System.out.println("Total " + imgUrlMap.size() + " images");
	}

	private void buildDirectory() {
		if (directoryLoc.exists()) {
			throw new RuntimeException("This file already exists: "
					+ directoryLoc.getAbsolutePath());
		} else {
			if (!directoryLoc.mkdir()) {
				throw new RuntimeException("Can't create the directory "
						+ directoryLoc.getAbsolutePath());
			}
		}
	}

	private void initImgUrlMap(List<String> imgUrlSet) {
		for (String urlStr : imgUrlSet) {
			initImgUrlMap(urlStr);
		}
		reDownloadMap = new ConcurrentHashMap<URL, String>(imgUrlMap);
	}
	
	private void initImgUrlMap(String urlStr) {
		URL url = null;
		String filename = "";
		try {
			url = new URL(urlStr);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		int lastIndex = urlStr.lastIndexOf("/");
		if (lastIndex != -1) {
			filename = urlStr.substring(lastIndex + 1, urlStr.length());
		}

		imgUrlMap.put(url, filename);
	}

	public ImgSourceExtractor getExtractor() {
		return extractor;
	}

	public void setExtractor(ImgSourceExtractor extractor) {
		this.extractor = extractor;
	}

	public File getDirectoryLoc() {
		return directoryLoc;
	}

	public void setDirectoryLoc(File directoryLoc) {
		this.directoryLoc = directoryLoc;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}
	
}
